import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import matplotlib.pyplot as plt
import seaborn as sns
import os
from matplotlib import font_manager as fm


# 改进的中文字体支持方案
def setup_chinese_font():
    """设置中文字体支持，自动适应不同操作系统"""
    try:
        # 尝试使用系统默认中文字体
        plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'STXihei', 'SimSun', 'Arial Unicode MS']
        plt.rcParams['axes.unicode_minus'] = False

        # 检查常见系统字体路径
        font_paths = [
            'C:/Windows/Fonts/simhei.ttf',  # Windows
            'C:/Windows/Fonts/msyh.ttc',  # Windows 微软雅黑
            '/System/Library/Fonts/PingFang.ttc',  # macOS
            '/Library/Fonts/Arial Unicode.ttf',  # macOS
            '/usr/share/fonts/truetype/droid/DroidSansFallbackFull.ttf'  # Linux
        ]

        # 添加当前目录作为备选
        current_dir = os.path.dirname(os.path.abspath(__file__))
        font_paths.append(os.path.join(current_dir, 'SimHei.ttf'))

        # 寻找可用的中文字体
        for path in font_paths:
            if os.path.exists(path):
                font_prop = fm.FontProperties(fname=path)
                plt.rcParams['font.sans-serif'] = [font_prop.get_name()]
                print(f"使用字体: {font_prop.get_name()}")
                return font_prop

        print("警告: 未找到中文字体文件，图表可能无法正确显示中文")
        return fm.FontProperties()

    except Exception as e:
        print(f"字体设置错误: {e}")
        return fm.FontProperties()


# 调用字体设置函数
font = setup_chinese_font()


def generate_mock_data():
    # 生成日期范围：2025-01-01 至 2025-02-05
    dates = pd.date_range(start='2025-01-01', end='2025-02-05')

    # 机构数据
    institutions = pd.DataFrame({
        'report_date': dates,
        'registered_capital': np.linspace(10000000, 10200000, len(dates)),
        'admitted_assets': np.linspace(50000000, 52000000, len(dates)),
        'admitted_liability': np.linspace(40000000, 41000000, len(dates)),
        'min_capital': [2000000] * len(dates),
        'total_assets': np.linspace(55000000, 57000000, len(dates)),
        'bad_debt_reserve': np.random.randint(50000, 80000, len(dates)),
        'impairment_reserve': np.random.randint(100000, 150000, len(dates))
    })

    # 保单数据
    policies = pd.DataFrame({
        'policy_date': dates,
        'total_premium': np.random.randint(500000, 800000, len(dates))
    })

    # 再保险数据
    reinsurance = pd.DataFrame({
        'reinsurance_date': dates,
        'ceded_premium': np.random.randint(50000, 100000, len(dates))  # 分出保费
    })

    # 准备金数据
    reserves = pd.DataFrame({
        'reserve_date': dates,
        'outstanding_claim_reserve': np.random.randint(3000000, 3500000, len(dates))
    })

    # 保险负债数据
    liabilities = pd.DataFrame({
        'liability_date': dates,
        'insurance_liability': np.random.randint(38000000, 40000000, len(dates)),
        'total_liability_equity': np.random.randint(60000000, 62000000, len(dates))
    })

    return institutions, policies, reinsurance, reserves, liabilities


def calculate_risk_metrics(institutions, policies, reinsurance, reserves, liabilities):
    # 合并数据集
    df = pd.merge(institutions, policies, left_on='report_date', right_on='policy_date', how='left')
    df = pd.merge(df, reinsurance, left_on='report_date', right_on='reinsurance_date', how='left')
    df = pd.merge(df, reserves, left_on='report_date', right_on='reserve_date', how='left')
    df = pd.merge(df, liabilities, left_on='report_date', right_on='liability_date', how='left')

    # 保留必要字段
    df = df[['report_date', 'registered_capital', 'total_premium', 'ceded_premium',
             'outstanding_claim_reserve', 'insurance_liability', 'total_liability_equity',
             'admitted_assets', 'admitted_liability', 'min_capital',
             'total_assets', 'bad_debt_reserve', 'impairment_reserve']]

    # 计算衍生字段
    df['net_premium'] = df['total_premium'] - df['ceded_premium']  # 自留保费
    df['actual_capital'] = df['admitted_assets'] - df['admitted_liability']  # 实际资本

    results = []
    # 使用'ME'代替'M'避免警告
    time_granularities = ['D', 'W', 'ME']  # 修改这里

    for granularity in time_granularities:
        # 分组聚合
        grouped = df.groupby(pd.Grouper(key='report_date', freq=granularity)).agg({
            'net_premium': 'sum',
            'ceded_premium': 'sum',
            'registered_capital': 'mean',
            'total_premium': 'sum',
            'outstanding_claim_reserve': 'last',
            'insurance_liability': 'last',
            'total_liability_equity': 'last',
            'actual_capital': 'last',
            'min_capital': 'last',
            'admitted_liability': 'last',
            'admitted_assets': 'last',
            'bad_debt_reserve': 'sum',
            'impairment_reserve': 'sum',
            'total_assets': 'last'
        }).reset_index()

        # 添加期初资本
        grouped['start_capital'] = grouped['actual_capital'].shift(1)

        # 计算指标
        grouped['retention_premium_ratio'] = grouped['net_premium'] / grouped['registered_capital'] * 100
        grouped['retention_ratio'] = grouped['net_premium'] / grouped['total_premium'] * 100
        grouped['outstanding_claim_ratio'] = grouped['outstanding_claim_reserve'] / grouped['registered_capital'] * 100
        grouped['insurance_liability_ratio'] = grouped['insurance_liability'] / grouped['total_liability_equity'] * 100
        grouped['solvency_ratio'] = grouped['actual_capital'] / grouped['min_capital'] * 100
        grouped['capital_change_ratio'] = (grouped['actual_capital'] - grouped['start_capital']) / grouped[
            'start_capital'] * 100
        grouped['admitted_liability_ratio'] = grouped['admitted_liability'] / grouped['admitted_assets'] * 100
        grouped['premium_scale_ratio'] = grouped['net_premium'] / grouped['actual_capital'] * 100
        grouped['impairment_reserve_ratio'] = (grouped['bad_debt_reserve'] + grouped['impairment_reserve']) / grouped[
            'total_assets'] * 100

        grouped['granularity'] = granularity
        results.append(grouped)

    return pd.concat(results)


def plot_risk_metrics(metrics_df):
    """
    绘制风险指标图表
    """
    # 为不同粒度创建子数据集
    daily_df = metrics_df[metrics_df['统计周期'] == '日']
    weekly_df = metrics_df[metrics_df['统计周期'] == '周']
    monthly_df = metrics_df[metrics_df['统计周期'] == '月']

    # 创建图表目录
    output_dir = f"人寿保险风险指标图表_{datetime.now().strftime('%Y%m%d')}"
    os.makedirs(output_dir, exist_ok=True)

    # 1. 偿付能力指标趋势图
    plt.figure(figsize=(14, 8))
    plt.subplot(2, 1, 1)
    sns.lineplot(data=daily_df, x='报告日期', y='偿付能力充足率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='偿付能力充足率(%)', label='周数据', linewidth=2.5)
    if not monthly_df.empty:
        sns.lineplot(data=monthly_df, x='报告日期', y='偿付能力充足率(%)', label='月数据', marker='o', markersize=8)
    plt.title('偿付能力充足率趋势', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 1, 2)
    sns.lineplot(data=daily_df, x='报告日期', y='实际资本变化率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='实际资本变化率(%)', label='周数据', linewidth=2.5)
    if not monthly_df.empty:
        sns.lineplot(data=monthly_df, x='报告日期', y='实际资本变化率(%)', label='月数据', marker='o', markersize=8)
    plt.title('实际资本变化率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.tight_layout()
    plt.savefig(f"{output_dir}/偿付能力指标趋势.png")
    plt.close()

    # 2. 保费相关指标
    plt.figure(figsize=(14, 10))
    plt.subplot(2, 2, 1)
    sns.lineplot(data=daily_df, x='报告日期', y='自留保费占净资产比(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='自留保费占净资产比(%)', label='周数据', linewidth=2.5)
    plt.title('自留保费占净资产比', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 2, 2)
    sns.lineplot(data=daily_df, x='报告日期', y='自留比率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='自留比率(%)', label='周数据', linewidth=2.5)
    plt.title('自留比率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 2, 3)
    sns.lineplot(data=daily_df, x='报告日期', y='保险业务收入规模率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='保险业务收入规模率(%)', label='周数据', linewidth=2.5)
    plt.title('保险业务收入规模率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 2, 4)
    sns.lineplot(data=daily_df, x='报告日期', y='未决赔款准备金对净资产比率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='未决赔款准备金对净资产比率(%)', label='周数据', linewidth=2.5)
    plt.title('未决赔款准备金对净资产比率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.tight_layout()
    plt.savefig(f"{output_dir}/保费相关指标.png")
    plt.close()

    # 3. 资产负债相关指标
    plt.figure(figsize=(14, 10))
    plt.subplot(2, 2, 1)
    sns.lineplot(data=daily_df, x='报告日期', y='保险负债占比(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='保险负债占比(%)', label='周数据', linewidth=2.5)
    plt.title('保险负债占比', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 2, 2)
    sns.lineplot(data=daily_df, x='报告日期', y='认可资产负债率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='认可资产负债率(%)', label='周数据', linewidth=2.5)
    plt.title('认可资产负债率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    plt.subplot(2, 2, 3)
    sns.lineplot(data=daily_df, x='报告日期', y='资产减值准备比率(%)', label='日数据')
    sns.lineplot(data=weekly_df, x='报告日期', y='资产减值准备比率(%)', label='周数据', linewidth=2.5)
    plt.title('资产减值准备比率', fontproperties=font)
    plt.ylabel('百分比(%)')
    plt.grid(True, linestyle='--', alpha=0.7)

    # 4. 所有指标周趋势对比
    plt.figure(figsize=(16, 10))
    # 过滤出需要绘制的指标列
    plot_columns = [
        '自留保费占净资产比(%)',
        '自留比率(%)',
        '未决赔款准备金对净资产比率(%)',
        '保险负债占比(%)',
        '偿付能力充足率(%)',
        '实际资本变化率(%)',
        '认可资产负债率(%)',
        '保险业务收入规模率(%)',
        '资产减值准备比率(%)'
    ]

    weekly_melt = weekly_df.melt(id_vars=['报告日期'],
                                 value_vars=plot_columns,
                                 var_name='指标',
                                 value_name='值')

    sns.lineplot(data=weekly_melt, x='报告日期', y='值', hue='指标')
    plt.title('关键风险指标周趋势对比', fontproperties=font, fontsize=16)
    plt.ylabel('百分比(%)')
    plt.xlabel('日期')
    plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.grid(True, linestyle='--', alpha=0.7)
    plt.tight_layout()
    plt.savefig(f"{output_dir}/所有指标周趋势对比.png")
    plt.close()

    # 5. 指标热力图
    plt.figure(figsize=(12, 8))
    # 只选择数值列计算相关性
    numeric_cols = weekly_df.select_dtypes(include=['float64', 'int64']).columns
    corr_df = weekly_df[numeric_cols].corr()
    sns.heatmap(corr_df, annot=True, fmt=".2f", cmap='coolwarm', center=0)
    plt.title('风险指标相关性热力图', fontproperties=font, fontsize=16)
    plt.tight_layout()
    plt.savefig(f"{output_dir}/指标相关性热力图.png")
    plt.close()

    return output_dir


def main():
    print("生成模拟数据...")
    institutions, policies, reinsurance, reserves, liabilities = generate_mock_data()

    print("计算风险指标...")
    metrics_df = calculate_risk_metrics(institutions, policies, reinsurance, reserves, liabilities)

    # 结果处理
    result_columns = [
        'report_date', 'granularity', 'retention_premium_ratio', 'retention_ratio',
        'outstanding_claim_ratio', 'insurance_liability_ratio', 'solvency_ratio',
        'capital_change_ratio', 'admitted_liability_ratio', 'premium_scale_ratio',
        'impairment_reserve_ratio'
    ]

    final_df = metrics_df[result_columns].copy()

    # 重命名列
    column_names = {
        'report_date': '报告日期',
        'granularity': '统计周期',
        'retention_premium_ratio': '自留保费占净资产比(%)',
        'retention_ratio': '自留比率(%)',
        'outstanding_claim_ratio': '未决赔款准备金对净资产比率(%)',
        'insurance_liability_ratio': '保险负债占比(%)',
        'solvency_ratio': '偿付能力充足率(%)',
        'capital_change_ratio': '实际资本变化率(%)',
        'admitted_liability_ratio': '认可资产负债率(%)',
        'premium_scale_ratio': '保险业务收入规模率(%)',
        'impairment_reserve_ratio': '资产减值准备比率(%)'
    }

    final_df.rename(columns=column_names, inplace=True)

    # 转换统计周期
    period_mapping = {'D': '日', 'W': '周', 'ME': '月'}  # ME对应月
    final_df['统计周期'] = final_df['统计周期'].map(period_mapping)

    # 导出结果
    output_file = f'人寿保险风险管理指标_{datetime.now().strftime("%Y%m%d")}.xlsx'
    print(f"导出结果到: {output_file}")
    final_df.to_excel(output_file, index=False, float_format='%.2f')

    # 生成风险指标图表
    print("生成风险指标图表...")
    try:
        chart_dir = plot_risk_metrics(final_df)
        print(f"图表已保存到目录: {chart_dir}")
    except Exception as e:
        print(f"生成图表时出错: {e}")

    print("处理完成!")


if __name__ == '__main__':
    main()